package com.bj58.mis.apm.collector.host;

import com.bj58.mis.apm.collector.BaseSchedulableCollector;
import org.hyperic.sigar.*;
import org.hyperic.sigar.cmd.Ps;
import org.hyperic.sigar.cmd.Shell;
import org.hyperic.sigar.shell.ShellCommandExecException;
import org.hyperic.sigar.shell.ShellCommandUsageException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

public class HostCollector extends BaseSchedulableCollector<HostModel> {
    static
    {
        //初始化动态链接库
        if(SigarLoader.IS_WIN32) {
            String filename="sigar-amd64-winnt.dll";
            init(filename);
        }else if(SigarLoader.IS_LINUX){
            String filename="libsigar-amd64-linux.so";
            init(filename);
        } else if(SigarLoader.IS_DARWIN){
            String filename="libsigar-universal64-macosx.dylib";
            init(filename);
        }
    }

    /**
     * 初始化动态链接库
     * @param filename 动态链接库的名字
     */
    public static void init(String filename){
        String path=System.getProperty("java.io.tmpdir")+File.separator+"HostCollector";
        File f=new File(path+File.separator+filename);

        //如果临时目录没有动态链接库文件,则从jar包中拷贝一份
        if(!f.exists()) {

            new File(path).mkdirs();
            InputStream in=Thread.currentThread().getContextClassLoader().getResourceAsStream("HostCollector/"+filename);
            FileOutputStream fos= null;
            try {
                fos = new FileOutputStream(f);
                byte[] data=new byte[1024];
                int bytesRead=0;
                while((bytesRead=in.read(data))>0){
                    fos.write(data,0,bytesRead);
                }
                fos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
            finally {
                try {
                    fos.close();
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        //将临时目录的动态链接库,添加到系统java.library.path
        System.setProperty("java.library.path",path+File.pathSeparator+System.getProperty("java.library.path"));
    }


    //上次采集的对象
    private HostModel lastHostModel=null;

    private HostModel.Item[] exclude=null;

    protected Logger logger = LoggerFactory.getLogger(this.getClass());


    public HostCollector(){

    }


    public HostModel collector(HostModel.Item...exclude) throws SigarException {
        this.exclude=exclude;
        return collector();
    }

    public HostModel collector() throws SigarException {
        HostModel hostModel=new HostModel();
        Sigar sigar=new Sigar();

        hostModel.setCollectTime(System.currentTimeMillis());
        if(this.exclude!=null){
            hostModel.getExclude().addAll(Arrays.asList(this.exclude));
        }

        if(!hostModel.getExclude().contains(HostModel.Item.CPU)) {
            collectorCpu(sigar,hostModel);
        }
        if(!hostModel.getExclude().contains(HostModel.Item.DF)) {
            collectorDf(sigar,hostModel);
        }
        if(!hostModel.getExclude().contains(HostModel.Item.MEM)) {
            collectorMem(sigar,hostModel);
        }
        if(!hostModel.getExclude().contains(HostModel.Item.NET)) {
            collectorNetwork(sigar,hostModel);
        }
        if(!hostModel.getExclude().contains(HostModel.Item.PROCSTAT)) {
            collectorProcStat(sigar,hostModel);
        }
        if(!hostModel.getExclude().contains(HostModel.Item.PS)) {
            collectorPs(sigar,hostModel);
        }
        if(!hostModel.getExclude().contains(HostModel.Item.SELFPS)) {
            collectorSelfPs(sigar,hostModel);
        }
        collectorInterval(hostModel);
        collectorSystem(hostModel);
        super.collect(hostModel);
        return hostModel;
    }

    private void collectorSystem(HostModel hostModel){
        Map<String, String> map = System.getenv();
        Properties props = System.getProperties();

        try {
            hostModel.setComputerHostName(InetAddress.getLocalHost().getHostName());
            hostModel.setComputerName(map.get("COMPUTERNAME"));
            hostModel.setUserDomain(map.get("USERDOMAIN"));
            hostModel.setUsername(map.get("USERNAME"));
            hostModel.setOsarch(String.valueOf(props.get("os.arch")));
            hostModel.setOsname(String.valueOf(props.get("os.name")));
            hostModel.setOsversion(String.valueOf(props.get("os.version")));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
    private void collectorInterval( HostModel hostModel) throws SigarException{

        if(lastHostModel!=null) {

            long interval=(hostModel.getCollectTime()-lastHostModel.getCollectTime())/1000;


            if(!hostModel.getExclude().contains(HostModel.Item.NET) && !lastHostModel.getExclude().contains(HostModel.Item.NET)) {
                Map<String, Long> networkstat = hostModel.getNetworkstat();
                networkstat.put("rxPacketsPSec", (networkstat.get("rxPackets") - lastHostModel.getNetworkstat().get("rxPackets")) / interval);
                networkstat.put("rxBytesPSec", (networkstat.get("rxBytes") - lastHostModel.getNetworkstat().get("rxBytes")) / interval);
                networkstat.put("txPacketsPSec", (networkstat.get("txPackets") - lastHostModel.getNetworkstat().get("txPackets")) / interval);
                networkstat.put("txBytesPSec", (networkstat.get("txBytes") - lastHostModel.getNetworkstat().get("txBytes")) / interval);
            }

            if(!hostModel.getExclude().contains(HostModel.Item.DF)&&!lastHostModel.getExclude().contains(HostModel.Item.DF)) {
                for (Map<String, String> disk : hostModel.getDf()) {
                    for (Map<String, String> lastDisk : lastHostModel.getDf()) {
                        if (disk.get("name").equals(lastDisk.get("name"))) {
                            disk.put("diskWritesPSec", String.valueOf((Long.valueOf(disk.get("diskWrites")).longValue() - Long.valueOf(lastDisk.get("diskWrites")).longValue()) / interval));
                            disk.put("diskReadBytesPSec", String.valueOf((Long.valueOf(disk.get("diskReadBytes")).longValue() - Long.valueOf(lastDisk.get("diskReadBytes")).longValue()) / interval));
                            disk.put("diskReadsPSec", String.valueOf((Long.valueOf(disk.get("diskReads")).longValue() - Long.valueOf(lastDisk.get("diskReads")).longValue()) / interval));
                            disk.put("diskWriteBytesPSec", String.valueOf((Long.valueOf(disk.get("diskWriteBytes")).longValue() - Long.valueOf(lastDisk.get("diskWriteBytes")).longValue()) / interval));

                            break;
                        }
                    }
                }
            }
        }
        lastHostModel=hostModel;
    }

    private void collectorSelfPs(Sigar sigar,HostModel hostModel) throws SigarException{
        long[] pids = Shell.getPids(sigar,new String[]{});
        for(long pid:pids){
            Map<String,String> map=getPsInfo(sigar,pid);
            if(map!=null) {
                hostModel.getSelfPs().putAll(map);
            }
        }
    }

    private void collectorPs(Sigar sigar,HostModel hostModel) throws SigarException{
        long[] pids =sigar.getProcList();
        for(long pid:pids){
            Map<String,String> map=getPsInfo(sigar,pid);
            if(map!=null) {
                hostModel.getPs().add(map);
            }
        }

    }
    private Map<String,String> getPsInfo(Sigar sigar,long pid){
        Map<String,String> map=new HashMap<String,String>();
        String cpuPerc = "?";

        List<String> info=null;
        try {
            info = Ps.getInfo(sigar, pid);
            //适配Mac系统
            if(info.size()!=9){
                return null;
            }
            String command= String.valueOf(info.get(8));
            String size= String.valueOf(info.get(3));
            String stime= String.valueOf(info.get(2));
            String user=String.valueOf(info.get(1));
            //过滤windows系统进程
            if(command.contains("C:\\windows") || command.contains("svchost")||command.contains("C:\\Windows")||"".equals(command))
            {
                return null;
            }
            //过滤Linux系统进程
            if(command.contains("[") && command.contains("]")){
                return null;
            }
            if(command.contains("/usr/lib/systemd/")|| command.startsWith("rcu") || command.startsWith("xfs")){
                return null;
            }

            //过滤Linux系统进程
            if("0".equals(size.trim())){
                return null;
            }

            if("???".equals(stime)){
                return null;
            }
            if("???".equals(user)){
                return null;
            }

        } catch (SigarException e) {
            return null;
        }
        try {
            ProcCpu cpu = sigar.getProcCpu(pid);
            cpuPerc = CpuPerc.format(cpu.getPercent());
        } catch (SigarException e) {
            //如果获取不到,则以问号填充
        }

        info.add(cpuPerc);

        String cpu=String.valueOf(info.get(9));
        if("?".equals(cpu)){
            return null;
        }

        map.put("pid", String.valueOf(info.get(0)));
        map.put("user", String.valueOf(info.get(1)));
        map.put("stime", String.valueOf(info.get(2)));
        map.put("size", String.valueOf(info.get(3)));
        map.put("rss", String.valueOf(info.get(4)));
        map.put("share", String.valueOf(info.get(5)));
        map.put("state", String.valueOf(info.get(6)));
        map.put("time", String.valueOf(info.get(7)));
        map.put("command", String.valueOf(info.get(8)));
        map.put("cpu", String.valueOf(info.get(9)));

        return map;
    }

    private void collectorProcStat(Sigar sigar,HostModel hostModel) throws SigarException{
        ProcStat procStat=sigar.getProcStat();
        hostModel.getProcStat().put("idle",procStat.getIdle());
        hostModel.getProcStat().put("running",procStat.getRunning());
        hostModel.getProcStat().put("sleeping",procStat.getSleeping());
        hostModel.getProcStat().put("stopped",procStat.getStopped());
        hostModel.getProcStat().put("threads",procStat.getThreads());
        hostModel.getProcStat().put("total",procStat.getTotal());
        hostModel.getProcStat().put("zombie",procStat.getZombie());
    }

    private void collectorMem(Sigar sigar,HostModel hostModel) throws SigarException{
        Mem mem   = sigar.getMem();
        Swap swap = sigar.getSwap();

        hostModel.getMem().put("total",Double.valueOf(mem.getTotal()));
        hostModel.getMem().put("used",Double.valueOf(mem.getUsed()));
        hostModel.getMem().put("free",Double.valueOf(mem.getFree()));
        hostModel.getMem().put("total",Double.valueOf(mem.getTotal()));
        hostModel.getMem().put("actualUsed",Double.valueOf(mem.getActualUsed()));
        hostModel.getMem().put("actualFree",Double.valueOf(mem.getActualFree()));
        hostModel.getMem().put("usedPercent",mem.getUsedPercent());
        hostModel.getMem().put("freePercent",mem.getFreePercent());

        hostModel.getSwap().put("total",Double.valueOf(swap.getTotal()));
        hostModel.getSwap().put("used",Double.valueOf(swap.getUsed()));
        hostModel.getSwap().put("free",Double.valueOf(swap.getFree()));
        hostModel.getSwap().put("pageIn",Double.valueOf(swap.getPageIn()));
        hostModel.getSwap().put("pageOut",Double.valueOf(swap.getPageOut()));
    }

    private void collectorNetwork(Sigar sigar,HostModel hostModel) throws SigarException{
        NetInterfaceConfig networkconfig=sigar.getNetInterfaceConfig(null);
        NetInterfaceStat networkstat=sigar.getNetInterfaceStat(networkconfig.getName());

        hostModel.getNetworkconfig().put("name",networkconfig.getName());
        hostModel.getNetworkconfig().put("hwaddr",networkconfig.getHwaddr());
        hostModel.getNetworkconfig().put("type",networkconfig.getType());
        hostModel.getNetworkconfig().put("description",networkconfig.getDescription());
        hostModel.getNetworkconfig().put("address",networkconfig.getAddress());
        hostModel.getNetworkconfig().put("destination",networkconfig.getDestination());
        hostModel.getNetworkconfig().put("broadcast",networkconfig.getBroadcast());
        hostModel.getNetworkconfig().put("netmask",networkconfig.getNetmask());

        hostModel.getNetworkstat().put("rxBytes",networkstat.getRxBytes());
        hostModel.getNetworkstat().put("rxPackets",networkstat.getRxPackets());
        hostModel.getNetworkstat().put("txBytes",networkstat.getTxBytes());
        hostModel.getNetworkstat().put("txPackets",networkstat.getTxPackets());
        hostModel.getNetworkstat().put("speed",networkstat.getSpeed());
    }

    private void collectorDf(Sigar sigar,HostModel hostModel) throws SigarException {
        List<Map<String,FileSystemUsage>> df=new ArrayList<Map<String,FileSystemUsage>>();
        FileSystem[] fileSystemArray=sigar.getFileSystemList();
        //Linux 根节点会出现两次,使用Set对挂载点去重
        Set<String> mountedList=new HashSet<String>();
        for(FileSystem fileSystem:fileSystemArray) {
            mountedList.add(fileSystem.getDirName());
        }
        for(String dirName :mountedList) {
            FileSystemUsage usage = sigar.getFileSystemUsage(dirName);
            //Sigar在Linux拿到的挂载点多余df命令显示的挂载点.
            //但是多出的挂载点,磁盘空间大小均为0,所以用磁盘空间大小过滤无用的显示信息.
            if(usage.getTotal()!=0) {
                Map<String, FileSystemUsage> map = new HashMap<String, FileSystemUsage>();
                map.put(dirName, usage);
                df.add(map);
            }
        }

        for(Map<String,FileSystemUsage> disk: df){
            for(Map.Entry<String, FileSystemUsage> entry:disk.entrySet()){
                Map<String,String> map=new HashMap<String,String>();
                FileSystemUsage usage=entry.getValue();
                map.put("name",entry.getKey());
                map.put("total", String.valueOf(usage.getTotal()));
                map.put("free", String.valueOf(usage.getFree()));
                map.put("used", String.valueOf(usage.getUsed()));
                map.put("avail", String.valueOf(usage.getAvail()));

                map.put("diskReads", String.valueOf(usage.getDiskReads()));
                map.put("diskWrites", String.valueOf(usage.getDiskWrites()));
                map.put("diskReadBytes", String.valueOf(usage.getDiskReadBytes()));
                map.put("diskWriteBytes", String.valueOf(usage.getDiskWriteBytes()));
                map.put("diskQueue", String.valueOf(usage.getDiskQueue()));
                map.put("usePercent", String.valueOf(usage.getUsePercent()));
                hostModel.getDf().add(map);
            }
        }
    }
    private void collectorCpu(Sigar sigar,HostModel hostModel) throws SigarException {
        CpuPerc cpu= sigar.getCpuPerc();
        CpuInfo[] cpuInfos =sigar.getCpuInfoList();
        CpuPerc[] cpuPercs=sigar.getCpuPercList();

        hostModel.getCpu().put("user", cpu.getUser());
        hostModel.getCpu().put("sys", cpu.getSys());
        hostModel.getCpu().put("nice", cpu.getNice());
        hostModel.getCpu().put("idle", cpu.getIdle());
        hostModel.getCpu().put("wait", cpu.getWait());
        hostModel.getCpu().put("irq",cpu.getIrq());
        hostModel.getCpu().put("softIrq",cpu.getSoftIrq());
        hostModel.getCpu().put("stolen",cpu.getStolen());
        hostModel.getCpu().put("combined",cpu.getCombined());

        hostModel.getCpuInfo().put("vendor", cpuInfos[0].getVendor());
        hostModel.getCpuInfo().put("model", cpuInfos[0].getModel());
        hostModel.getCpuInfo().put("Mhz", String.valueOf(cpuInfos[0].getMhz()));
        hostModel.getCpuInfo().put("totalCpus", String.valueOf(cpuInfos[0].getTotalCores()));

        for(CpuPerc cpuPerc: cpuPercs){
            Map<String,Double> map=new HashMap<String,Double>();
            map.put("user", cpuPerc.getUser());
            map.put("sys", cpuPerc.getSys());
            map.put("nice", cpuPerc.getNice());
            map.put("idle", cpuPerc.getIdle());
            map.put("wait", cpuPerc.getWait());
            map.put("irq",cpuPerc.getIrq());
            map.put("softIrq",cpuPerc.getSoftIrq());
            map.put("stolen",cpuPerc.getStolen());
            map.put("combined",cpuPerc.getCombined());
            hostModel.getCpuCoreInfo().add(map);
        }
    }


    public static void main(String[] args) throws SigarException, InterruptedException, ShellCommandExecException, ShellCommandUsageException {
        new Thread(new Runnable(){
            public void run() {

                while (true) {
                    HostCollector h = new HostCollector();
                    try {
                        HostModel model =h.collector();
                        System.out.println(model.toString());
                    } catch (SigarException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }


    @Override
    public void schedule() {
        try {
            collector();
        } catch (SigarException e) {
            e.printStackTrace();
        }
    }
}

